import os
import hashlib
import uuid
import time
import datetime
import shutil
import mysql.connector
from mysql.connector import Error
from flask import Flask, render_template, request, redirect, url_for, session, flash, jsonify, send_from_directory, abort, g
import config
from threading import Timer
import math
from functools import wraps
import zipfile
import threading

app = Flask(__name__)
app.config['UPLOAD_FOLDER'] = config.UPLOAD_FOLDER
app.config['MAX_CONTENT_LENGTH'] = config.MAX_CONTENT_LENGTH
app.secret_key = config.SECRET_KEY

os.makedirs(app.config['UPLOAD_FOLDER'], exist_ok=True)

# 登录验证装饰器
def login_required(f):
    @wraps(f)  # 新增
    def decorated_function(*args, **kwargs):
        if 'user_id' not in session:
            flash('请先登录', 'error')
            return redirect(url_for('login', next=request.url))
        return f(*args, **kwargs)
    return decorated_function

# 管理员验证装饰器
def admin_required(f):
    @wraps(f)  # 新增
    def decorated_function(*args, **kwargs):
        if 'user_id' not in session or not session.get('is_admin', False):
            flash('没有管理员权限', 'error')
            return redirect(url_for('dashboard'))
        return f(*args, **kwargs)
    return decorated_function

def get_db_connection():
    """获取数据库连接"""
    connection = None
    try:
        connection = mysql.connector.connect(** config.DB_CONFIG)
    except Error as e:
        print(f"数据库连接错误: {e}")
    return connection

def init_db():
    """初始化数据库"""
    conn = get_db_connection()
    if conn:
        cursor = conn.cursor()
        
        # 创建用户表
        cursor.execute('''
        CREATE TABLE IF NOT EXISTS users (
            id INT AUTO_INCREMENT PRIMARY KEY,
            email VARCHAR(255) UNIQUE,
            password_hash VARCHAR(255),
            machine_code VARCHAR(255) UNIQUE,
            storage_limit BIGINT NOT NULL DEFAULT %s,
            used_storage BIGINT NOT NULL DEFAULT 0,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            is_admin BOOLEAN DEFAULT FALSE
        )
        ''', (config.DEFAULT_STORAGE_LIMIT,))
        
        # 创建文件夹表
        cursor.execute('''
        CREATE TABLE IF NOT EXISTS folders (
            id INT AUTO_INCREMENT PRIMARY KEY,
            user_id INT NOT NULL,
            name VARCHAR(255) NOT NULL,
            parent_id INT,
            created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
            FOREIGN KEY (parent_id) REFERENCES folders(id) ON DELETE CASCADE
        )
        ''')
        
        # 创建文件表
        cursor.execute('''
        CREATE TABLE IF NOT EXISTS files (
            id INT AUTO_INCREMENT PRIMARY KEY,
            user_id INT NOT NULL,
            folder_id INT,
            filename VARCHAR(255) NOT NULL,
            file_path VARCHAR(255) NOT NULL,
            file_size BIGINT NOT NULL,
            upload_time TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
            is_deleted BOOLEAN DEFAULT FALSE,
            FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
            FOREIGN KEY (folder_id) REFERENCES folders(id) ON DELETE CASCADE
        )
        ''')
        
        # 创建管理员用户（如果不存在）
        cursor.execute('SELECT id FROM users WHERE email = %s', (config.ADMIN_EMAIL,))
        if not cursor.fetchone():
            admin_password = hashlib.sha256('admin123'.encode()).hexdigest()  # 默认管理员密码
            cursor.execute('''
            INSERT INTO users (email, password_hash, storage_limit, is_admin) 
            VALUES (%s, %s, %s, TRUE)
            ''', (config.ADMIN_EMAIL, admin_password, 100 * 1024 * 1024 * 1024))  # 管理员100GB空间        
        conn.commit()
        cursor.close()
        conn.close()

def get_machine_code():
    """生成机器码（基于硬件信息）"""
    hardware_info = str(uuid.getnode()).encode()
    return hashlib.md5(hardware_info).hexdigest()

def get_computer_name():
    """获取计算机名"""
    return os.environ.get('COMPUTERNAME', 'Unknown')

@app.route('/login', methods=['GET', 'POST'])
def login():
    if 'user_id' in session:
        return redirect(url_for('dashboard'))
        
    next_page = request.args.get('next', url_for('dashboard'))
    
    if request.method == 'POST':
        # 邮箱登录
        if 'email' in request.form:
            email = request.form['email']
            password = request.form['password']
            conn = get_db_connection()
            if conn:
                cursor = conn.cursor(dictionary=True)
                cursor.execute('SELECT * FROM users WHERE email = %s', (email,))
                user = cursor.fetchone()
                cursor.close()
                conn.close()
                if user and hashlib.sha256(password.encode()).hexdigest() == user['password_hash']:
                    session['user_id'] = user['id']
                    session['is_admin'] = user['is_admin']
                    session['email'] = user['email']
                    flash('登录成功', 'success')
                    return redirect(next_page)
                else:
                    flash('邮箱或密码错误', 'error')
        
        # 机器码登录一键登录
        elif 'use_machine_code' in request.form:
            machine_code = get_machine_code()
            computer_name = get_computer_name()
            conn = get_db_connection()
            if conn:
                cursor = conn.cursor(dictionary=True)
                cursor.execute('SELECT * FROM users WHERE machine_code = %s', (machine_code,))
                user = cursor.fetchone()
                if not user:
                    # 自动注册并登录，用户名为计算机名
                    email = computer_name
                    password_hash = hashlib.sha256(machine_code.encode()).hexdigest()
                    cursor.execute('''
                    INSERT INTO users (email, password_hash, machine_code, storage_limit)
                    VALUES (%s, %s, %s, %s)
                    ''', (email, password_hash, machine_code, config.DEFAULT_STORAGE_LIMIT))
                    user_id = cursor.lastrowid
                    user_folder = os.path.join(app.config['UPLOAD_FOLDER'], str(user_id))
                    os.makedirs(user_folder, exist_ok=True)
                    conn.commit()
                    cursor.execute('SELECT * FROM users WHERE id = %s', (user_id,))
                    user = cursor.fetchone()
                cursor.close()
                conn.close()
                session['user_id'] = user['id']
                session['is_admin'] = user['is_admin']
                session['email'] = user['email']
                flash('机器码登录成功', 'success')
                return redirect(next_page)
    machine_code = get_machine_code()
    computer_name = get_computer_name()
    return render_template('login.html', machine_code=machine_code, computer_name=computer_name)

@app.route('/register', methods=['GET', 'POST'])
def register():
    if 'user_id' in session:
        return redirect(url_for('dashboard'))
        
    if request.method == 'POST':
        email = request.form['email']
        password = request.form['password']
        confirm_password = request.form['confirm_password']
        if password != confirm_password:
            flash('两次密码输入不一致', 'error')
            return render_template('register.html')
        if len(password) < 6:
            flash('密码长度不能少于6位', 'error')
            return render_template('register.html')
        machine_code = get_machine_code()
        conn = get_db_connection()
        if conn:
            cursor = conn.cursor()
            cursor.execute('SELECT id FROM users WHERE email = %s', (email,))
            if cursor.fetchone():
                flash('该邮箱已被注册', 'error')
                cursor.close()
                conn.close()
                return render_template('register.html')
            cursor.execute('SELECT id FROM users WHERE machine_code = %s', (machine_code,))
            if cursor.fetchone():
                flash('该设备已关联其他账户', 'error')
                cursor.close()
                conn.close()
                return render_template('register.html')
            password_hash = hashlib.sha256(password.encode()).hexdigest()
            cursor.execute('''
            INSERT INTO users (email, password_hash, machine_code, storage_limit)
            VALUES (%s, %s, %s, %s)
            ''', (email, password_hash, machine_code, config.DEFAULT_STORAGE_LIMIT))
            user_id = cursor.lastrowid
            user_folder = os.path.join(app.config['UPLOAD_FOLDER'], str(user_id))
            os.makedirs(user_folder, exist_ok=True)
            conn.commit()
            cursor.close()
            conn.close()
            flash('注册成功，请登录', 'success')
            return redirect(url_for('login'))
    return render_template('register.html')

@app.route('/dashboard')
@app.route('/dashboard/<int:folder_id>')
@login_required
def dashboard(folder_id=None):
    user_id = session['user_id']
    conn = get_db_connection()
    files = []
    current_folder = None
    folder_path = get_folder_path(folder_id)
    
    if conn:
        cursor = conn.cursor(dictionary=True)
        
        # 获取用户信息
        cursor.execute('SELECT * FROM users WHERE id = %s', (user_id,))
        user = cursor.fetchone()
        
        # 获取当前文件夹信息
        if folder_id:
            cursor.execute('SELECT * FROM folders WHERE id = %s AND user_id = %s', (folder_id, user_id))
            current_folder = cursor.fetchone()
            if not current_folder:
                flash('文件夹不存在', 'error')
                return redirect(url_for('dashboard'))
        
        # 获取用户文件夹
        folders = get_user_folders(user_id, folder_id)
        
        # 获取用户文件（支持文件夹筛选）
        if folder_id:
            cursor.execute('''
            SELECT * FROM files WHERE user_id = %s AND folder_id = %s AND is_deleted = 0
            ORDER BY upload_time DESC
            ''', (user_id, folder_id))
        else:
            cursor.execute('''
            SELECT * FROM files WHERE user_id = %s AND folder_id IS NULL AND is_deleted = 0
            ORDER BY upload_time DESC
            ''', (user_id,))
        files = cursor.fetchall()
        
        # 格式化文件大小
        for file in files:
            file['formatted_size'] = format_size(file['file_size'])
        
        cursor.close()
        conn.close()
        
        # 计算存储空间使用情况
        storage_used = format_size(user['used_storage'])
        storage_limit = format_size(user['storage_limit'])
        usage_percent = (user['used_storage'] / user['storage_limit']) * 100 if user['storage_limit'] > 0 else 0
        
        # 存储空间状态
        storage_status = 'normal'
        if usage_percent >= 90:
            storage_status = 'danger'
        elif usage_percent >= config.STORAGE_WARNING_THRESHOLD:
            storage_status = 'warning'
        
        # 存储警告
        if usage_percent >= config.STORAGE_WARNING_THRESHOLD:
            flash(f'存储空间不足 {100 - usage_percent:.1f}%，请及时清理', 'warning')
        
        return render_template('dashboard.html',
                             files=files,
                             folders=folders,
                             current_folder=current_folder,
                             folder_path=folder_path,
                             storage_used=storage_used,
                             storage_limit=storage_limit,
                             usage_percent=usage_percent,
                             storage_status=storage_status,
                             is_admin=session.get('is_admin', False),
                             max_content_length=app.config['MAX_CONTENT_LENGTH'])  # 传递 max_content_length
    
    return render_template('dashboard.html', files=[], folders=[], max_content_length=app.config['MAX_CONTENT_LENGTH'])  # 新增

@app.route('/create-folder', methods=['POST'])
@login_required
def create_folder():
    user_id = session['user_id']
    folder_name = request.form.get('folder_name')
    parent_id = request.form.get('parent_id', None)
    if parent_id == '' or parent_id is None:
        parent_id = None
    else:
        parent_id = int(parent_id)
    if not folder_name:
        flash('文件夹名称不能为空', 'error')
        return redirect(url_for('dashboard', folder_id=parent_id))
    conn = get_db_connection()
    if conn:
        cursor = conn.cursor()
        # 检查同名文件夹
        if parent_id:
            cursor.execute('''
            SELECT id FROM folders WHERE user_id = %s AND parent_id = %s AND name = %s
            ''', (user_id, parent_id, folder_name))
        else:
            cursor.execute('''
            SELECT id FROM folders WHERE user_id = %s AND parent_id IS NULL AND name = %s
            ''', (user_id, folder_name))
        if cursor.fetchone():
            flash('该文件夹名称已存在', 'error')
            cursor.close()
            conn.close()
            return redirect(url_for('dashboard', folder_id=parent_id))
        # 创建文件夹数据库记录
        cursor.execute('''
        INSERT INTO folders (user_id, name, parent_id)
        VALUES (%s, %s, %s)
        ''', (user_id, folder_name, parent_id))
        folder_id = cursor.lastrowid
        conn.commit()
        cursor.close()
        conn.close()
        # 在物理目录下创建文件夹
        user_folder = os.path.join(app.config['UPLOAD_FOLDER'], str(user_id))
        if parent_id:
            # 获取父文件夹名称
            conn2 = get_db_connection()
            cursor2 = conn2.cursor(dictionary=True)
            cursor2.execute('SELECT name FROM folders WHERE id = %s', (parent_id,))
            parent_folder = cursor2.fetchone()
            cursor2.close()
            conn2.close()
            if parent_folder:
                folder_path = os.path.join(user_folder, parent_folder['name'], folder_name)
            else:
                folder_path = os.path.join(user_folder, folder_name)
        else:
            folder_path = os.path.join(user_folder, folder_name)
        os.makedirs(folder_path, exist_ok=True)
        flash('文件夹创建成功', 'success')
    return redirect(url_for('dashboard', folder_id=parent_id))

@app.route('/delete-folder/<int:folder_id>', methods=['POST'])
@login_required
def delete_folder(folder_id):
    user_id = session['user_id']
    conn = get_db_connection()
    
    if conn:
        cursor = conn.cursor(dictionary=True)
        
        # 检查文件夹是否属于当前用户
        cursor.execute('SELECT * FROM folders WHERE id = %s AND user_id = %s', (folder_id, user_id))
        folder = cursor.fetchone()
        
        if folder:
            # 获取文件夹内的所有文件
            cursor.execute('''
            SELECT id, file_path, file_size FROM files 
            WHERE user_id = %s AND folder_id = %s AND is_deleted = 0
            ''', (user_id, folder_id))
            files = cursor.fetchall()
            
            # 计算总文件大小
            total_size = sum(file['file_size'] for file in files)
            
            # 删除文件
            for file in files:
                if os.path.exists(file['file_path']):
                    try:
                        os.remove(file['file_path'])
                    except Exception as e:
                        print(f'删除文件失败: {str(e)}')
            
            # 删除文件夹及其子文件夹和文件（通过外键级联删除）
            cursor.execute('DELETE FROM folders WHERE id = %s', (folder_id,))
            
            # 更新用户已用空间
            cursor.execute('''
            UPDATE users SET used_storage = used_storage - %s 
            WHERE id = %s
            ''', (total_size, user_id))
            
            conn.commit()
            flash('文件夹及其中的文件已删除', 'success')
        
        cursor.close()
        conn.close()
    
    parent_id = folder.get('parent_id') if folder else None
    return redirect(url_for('dashboard', folder_id=parent_id))

def create_folder_recursive(user_id, folder_path_parts, parent_id=None, conn=None):
    """
    递归创建文件夹数据库记录，返回最深层文件夹ID
    folder_path_parts: ['A', 'B', 'C'] 表示 A/B/C
    """
    cursor = conn.cursor(dictionary=True)
    current_parent = parent_id
    folder_id = None
    for folder_name in folder_path_parts:
        # 检查是否已存在
        if current_parent:
            cursor.execute('SELECT id FROM folders WHERE user_id = %s AND parent_id = %s AND name = %s',
                           (user_id, current_parent, folder_name))
        else:
            cursor.execute('SELECT id FROM folders WHERE user_id = %s AND parent_id IS NULL AND name = %s',
                           (user_id, folder_name))
        row = cursor.fetchone()
        if row:
            folder_id = row['id']
        else:
            cursor.execute('INSERT INTO folders (user_id, name, parent_id) VALUES (%s, %s, %s)',
                           (user_id, folder_name, current_parent))
            folder_id = cursor.lastrowid
            conn.commit()
        current_parent = folder_id
    cursor.close()
    return folder_id

@app.route('/upload', methods=['POST'])
@login_required
def upload_file():
    user_id = session['user_id']
    folder_id = request.form.get('folder_id', None)
    upload_type = request.form.get('upload_type', '')
    if folder_id == '' or folder_id is None:
        folder_id = None
    else:
        folder_id = int(folder_id)
    files = request.files.getlist('file')
    if not files or all(f.filename == '' for f in files):
        flash('未选择文件', 'error')
        return redirect(url_for('dashboard', folder_id=folder_id))
    conn = get_db_connection()
    if not conn:
        flash('数据库连接失败', 'error')
        return redirect(url_for('dashboard', folder_id=folder_id))
    cursor = conn.cursor(dictionary=True)
    cursor.execute('SELECT storage_limit, used_storage FROM users WHERE id = %s', (user_id,))
    user = cursor.fetchone()
    total_new_size = 0
    file_infos = []
    for file in files:
        file.seek(0, os.SEEK_END)
        file_size = file.tell()
        file.seek(0)
        total_new_size += file_size
        file_infos.append((file, file_size))
    if user['used_storage'] + total_new_size > user['storage_limit']:
        flash('存储空间不足', 'error')
        cursor.close()
        conn.close()
        return redirect(url_for('dashboard', folder_id=folder_id))
    user_folder = os.path.join(app.config['UPLOAD_FOLDER'], str(user_id))
    os.makedirs(user_folder, exist_ok=True)

    # 文件夹上传逻辑（支持多层嵌套，采用首层目录为文件夹名）
    if upload_type == 'folder':
        folder_base = None
        for file, file_size in file_infos:
            filename = file.filename.replace("\\", "/")
            parts = filename.split("/")
            if len(parts) > 1:
                folder_parts = parts[:-1]
                file_name_only = parts[-1]
                if not folder_base:
                    folder_base = folder_parts[0]
                # 递归创建文件夹数据库记录
                folder_db_id = create_folder_recursive(user_id, folder_parts, folder_id, conn)
                folder_path = os.path.join(user_folder, *folder_parts)
                os.makedirs(folder_path, exist_ok=True)
                save_path = os.path.join(folder_path, file_name_only)
                counter = 1
                orig_save_path = save_path
                while os.path.exists(save_path):
                    name, ext = os.path.splitext(file_name_only)
                    save_path = os.path.join(folder_path, f"{name}_{counter}{ext}")
                    counter += 1
                file.save(save_path)
                cursor2 = conn.cursor()
                cursor2.execute('''
                    INSERT INTO files (user_id, folder_id, filename, file_path, file_size, is_deleted)
                    VALUES (%s, %s, %s, %s, %s, 0)
                ''', (user_id, folder_db_id, file_name_only, save_path, file_size))
                cursor2.execute('''
                    UPDATE users SET used_storage = used_storage + %s 
                    WHERE id = %s
                ''', (file_size, user_id))
                cursor2.close()
            else:
                file_name_only = parts[0]
                folder_db_id = folder_id
                save_path = os.path.join(user_folder, file_name_only)
                counter = 1
                orig_save_path = save_path
                while os.path.exists(save_path):
                    name, ext = os.path.splitext(file_name_only)
                    save_path = os.path.join(user_folder, f"{name}_{counter}{ext}")
                    counter += 1
                file.save(save_path)
                cursor2 = conn.cursor()
                cursor2.execute('''
                    INSERT INTO files (user_id, folder_id, filename, file_path, file_size, is_deleted)
                    VALUES (%s, %s, %s, %s, %s, 0)
                ''', (user_id, folder_db_id, file_name_only, save_path, file_size))
                cursor2.execute('''
                    UPDATE users SET used_storage = used_storage + %s 
                    WHERE id = %s
                ''', (file_size, user_id))
                cursor2.close()
        conn.commit()
        cursor.close()
        conn.close()
        # 上传文件夹后，重新压缩（删除原zip，后台重新生成）
        if folder_base:
            # 获取最顶层文件夹id
            conn3 = get_db_connection()
            cursor3 = conn3.cursor(dictionary=True)
            cursor3.execute('SELECT id FROM folders WHERE user_id = %s AND name = %s', (user_id, folder_base))
            folder_row = cursor3.fetchone()  # 只调用一次，不再调用 fetchall()
            # 不要再调用 fetchall()，直接关闭
            cursor3.close()
            conn3.close()
            if folder_row:
                remove_folder_zip(user_id, folder_row['id'], folder_base)
                schedule_zip_folder(user_id, folder_row['id'], folder_base)
        flash('文件夹上传成功', 'success')
        return redirect(url_for('dashboard', folder_id=folder_id))

    # 文件上传逻辑（归档到当前文件夹或根目录）
    for file, file_size in file_infos:
        filename = file.filename.replace("\\", "/")
        file_name_only = os.path.basename(filename)
        if folder_id:
            folder_path = os.path.join(user_folder)
            conn2 = get_db_connection()
            cursor2 = conn2.cursor(dictionary=True)
            cursor2.execute('SELECT name, parent_id FROM folders WHERE id = %s', (folder_id,))
            folder = cursor2.fetchone()
            cursor2.close()
            conn2.close()
            folder_names = []
            current_id = folder_id
            while folder:
                folder_names.insert(0, folder['name'])
                if folder['parent_id']:
                    conn3 = get_db_connection()
                    cursor3 = conn3.cursor(dictionary=True)
                    cursor3.execute('SELECT name, parent_id FROM folders WHERE id = %s', (folder['parent_id'],))
                    folder = cursor3.fetchone()
                    cursor3.close()
                    conn3.close()
                else:
                    folder = None
            folder_path = os.path.join(user_folder, *folder_names)
            os.makedirs(folder_path, exist_ok=True)
            save_path = os.path.join(folder_path, file_name_only)
        else:
            save_path = os.path.join(user_folder, file_name_only)
        counter = 1
        orig_save_path = save_path
        while os.path.exists(save_path):
            name, ext = os.path.splitext(file_name_only)
            save_path = os.path.join(os.path.dirname(save_path), f"{name}_{counter}{ext}")
            counter += 1
        file.save(save_path)
        cursor2 = conn.cursor()
        cursor2.execute('''
            INSERT INTO files (user_id, folder_id, filename, file_path, file_size, is_deleted)
            VALUES (%s, %s, %s, %s, %s, 0)
        ''', (user_id, folder_id, file_name_only, save_path, file_size))
        cursor2.execute('''
            UPDATE users SET used_storage = used_storage + %s 
            WHERE id = %s
        ''', (file_size, user_id))
        cursor2.close()
    conn.commit()
    cursor.close()
    conn.close()
    # 上传文件到文件夹后，删除原有zip，重新压缩
    if folder_id:
        # 获取文件夹名称
        conn4 = get_db_connection()
        cursor4 = conn4.cursor(dictionary=True)
        cursor4.execute('SELECT name FROM folders WHERE id = %s', (folder_id,))
        folder = cursor4.fetchone()
        cursor4.close()
        conn4.close()
        if folder:
            remove_folder_zip(user_id, folder_id, folder['name'])
            schedule_zip_folder(user_id, folder_id, folder['name'])
    flash('文件上传成功', 'success')
    return redirect(url_for('dashboard', folder_id=folder_id))

@app.route('/search', methods=['GET'])
@login_required
def search_files():
    user_id = session['user_id']
    query = request.args.get('query', '')
    
    if not query:
        return redirect(url_for('dashboard'))
    
    conn = get_db_connection()
    files = []
    folders = []
    
    if conn:
        cursor = conn.cursor(dictionary=True)
        
        # 搜索文件
        cursor.execute('''
        SELECT * FROM files WHERE user_id = %s AND filename LIKE %s AND is_deleted = 0
        ORDER BY upload_time DESC
        ''', (user_id, f'%{query}%'))
        files = cursor.fetchall()
        
        # 格式化文件大小
        for file in files:
            file['formatted_size'] = format_size(file['file_size'])
        
        # 搜索文件夹
        cursor.execute('''
        SELECT * FROM folders WHERE user_id = %s AND name LIKE %s
        ORDER BY created_at DESC
        ''', (user_id, f'%{query}%'))
        folders = cursor.fetchall()
        
        # 获取用户存储信息
        cursor.execute('SELECT storage_limit, used_storage FROM users WHERE id = %s', (user_id,))
        user = cursor.fetchone()
        
        cursor.close()
        conn.close()
        
        storage_used = format_size(user['used_storage'])
        storage_limit = format_size(user['storage_limit'])
        usage_percent = (user['used_storage'] / user['storage_limit']) * 100 if user['storage_limit'] > 0 else 0
        
        storage_status = 'normal'
        if usage_percent >= 90:
            storage_status = 'danger'
        elif usage_percent >= config.STORAGE_WARNING_THRESHOLD:
            storage_status = 'warning'
        
        return render_template('dashboard.html', 
                             files=files,
                             folders=folders,
                             search_query=query,
                             storage_used=storage_used,
                             storage_limit=storage_limit,
                             usage_percent=usage_percent,
                             storage_status=storage_status,
                             is_admin=session.get('is_admin', False),
                             max_content_length=app.config['MAX_CONTENT_LENGTH'])
    
    return render_template('dashboard.html', files=[], folders=[], max_content_length=app.config['MAX_CONTENT_LENGTH'])  # 新增

@app.route('/download/<int:file_id>')
@login_required
def download_file(file_id):
    user_id = session['user_id']
    conn = get_db_connection()
    if conn:
        cursor = conn.cursor(dictionary=True)
        cursor.execute('''
        SELECT * FROM files WHERE id = %s AND user_id = %s AND is_deleted = 0
        ''', (file_id, user_id))
        file = cursor.fetchone()
        cursor.close()
        conn.close()
        
        if file and os.path.exists(file['file_path']):
            directory, filename = os.path.split(file['file_path'])
            return send_from_directory(directory, filename, as_attachment=True)
    
    abort(404)

@app.route('/delete/<int:file_id>', methods=['POST'])
@login_required
def delete_file(file_id):
    user_id = session['user_id']
    conn = get_db_connection()
    folder_id = None
    
    if conn:
        cursor = conn.cursor(dictionary=True)
        
        # 获取文件信息
        cursor.execute('''
        SELECT * FROM files WHERE id = %s AND user_id = %s
        ''', (file_id, user_id))
        file = cursor.fetchone()
        
        if file:
            folder_id = file.get('folder_id')
            
            # 移动到回收站
            cursor.execute('UPDATE files SET is_deleted = 1 WHERE id = %s', (file_id,))
            conn.commit()
            flash('文件已移入回收站', 'success')
        
        cursor.close()
        conn.close()
    
    return redirect(url_for('dashboard', folder_id=folder_id))

@app.route('/recycle')
@login_required
def recycle_bin():
    user_id = session['user_id']
    conn = get_db_connection()
    files = []
    if conn:
        cursor = conn.cursor(dictionary=True)
        cursor.execute('SELECT * FROM files WHERE user_id = %s AND is_deleted = 1 ORDER BY upload_time DESC', (user_id,))
        files = cursor.fetchall()
        for file in files:
            file['formatted_size'] = format_size(file['file_size'])
        cursor.close()
        conn.close()
    return render_template('recycle.html', files=files)

@app.route('/restore/<int:file_id>', methods=['POST'])
@login_required
def restore_file(file_id):
    user_id = session['user_id']
    conn = get_db_connection()
    if conn:
        cursor = conn.cursor()
        cursor.execute('UPDATE files SET is_deleted = 0 WHERE id = %s AND user_id = %s', (file_id, user_id))
        conn.commit()
        cursor.close()
        conn.close()
        flash('文件已恢复', 'success')
    return redirect(url_for('recycle_bin'))

@app.route('/purge/<int:file_id>', methods=['POST'])
@login_required
def purge_file(file_id):
    user_id = session['user_id']
    conn = get_db_connection()
    if conn:
        cursor = conn.cursor(dictionary=True)
        cursor.execute('SELECT * FROM files WHERE id = %s AND user_id = %s', (file_id, user_id))
        file = cursor.fetchone()
        if file:
            if os.path.exists(file['file_path']):
                try:
                    os.remove(file['file_path'])
                except Exception as e:
                    flash(f'彻底删除文件失败: {str(e)}', 'error')
                    conn.close()
                    return redirect(url_for('recycle_bin'))
            cursor.execute('DELETE FROM files WHERE id = %s', (file_id,))
            cursor.execute('''
            UPDATE users SET used_storage = used_storage - %s 
            WHERE id = %s
            ''', (file['file_size'], user_id))
            conn.commit()
            flash('文件已彻底删除', 'success')
        cursor.close()
        conn.close()
    return redirect(url_for('recycle_bin'))

# 回收站保留天数（可在管理员后台设置）
RECYCLE_DAYS = getattr(config, 'RECYCLE_DAYS', 7)
def cleanup_old_files():
    """清理过期文件和回收站文件"""
    conn = get_db_connection()
    if conn:
        cursor = conn.cursor(dictionary=True)
        recycle_days_ago = datetime.datetime.now() - datetime.timedelta(days=RECYCLE_DAYS)
        cursor.execute('''
        SELECT id, file_path, user_id, file_size FROM files 
        WHERE is_deleted = 1 AND upload_time < %s
        ''', (recycle_days_ago,))
        old_files = cursor.fetchall()
        for file in old_files:
            try:
                if os.path.exists(file['file_path']):
                    os.remove(file['file_path'])
                cursor.execute('DELETE FROM files WHERE id = %s', (file['id'],))
                cursor.execute('''
                UPDATE users SET used_storage = used_storage - %s 
                WHERE id = %s
                ''', (file['file_size'], file['user_id']))
            except Exception as e:
                print(f"回收站清理错误: {e}")
        conn.commit()
        cursor.close()
        conn.close()
    Timer(86400, cleanup_old_files).start()

# 管理员设置回收站保留天数
@app.route('/admin', methods=['GET', 'POST'])
@admin_required
def admin_panel():
    global RECYCLE_DAYS
    if request.method == 'POST':
        user_id = request.form.get('user_id')
        new_limit = request.form.get('storage_limit')
        set_admin = request.form.get('set_admin')
        delete_user = request.form.get('delete_user')
        recycle_days = request.form.get('recycle_days')
        try:
            conn = get_db_connection()
            if conn:
                cursor = conn.cursor()
                if new_limit:
                    size_unit = request.form.get('size_unit', 'GB')
                    units = {'GB': 1024**3, 'MB': 1024**2, 'KB': 1024}
                    new_limit_bytes = int(float(new_limit) * units[size_unit])
                    cursor.execute('UPDATE users SET storage_limit = %s WHERE id = %s', (new_limit_bytes, user_id))
                    flash('存储空间已更新', 'success')
                if set_admin:
                    cursor.execute('UPDATE users SET is_admin = %s WHERE id = %s', (set_admin == '1', user_id))
                    flash('管理员权限已更新', 'success')
                if delete_user:
                    cursor.execute('DELETE FROM users WHERE id = %s', (user_id,))
                    flash('用户已删除', 'success')
                if recycle_days:
                    try:
                        RECYCLE_DAYS = int(recycle_days)
                        flash('回收站保留天数已更新', 'success')
                    except Exception as e:
                        flash(f'设置失败: {str(e)}', 'error')
                conn.commit()
                cursor.close()
                conn.close()
        except Exception as e:
            flash(f'操作失败: {str(e)}', 'error')
    users = []
    conn = get_db_connection()
    if conn:
        cursor = conn.cursor(dictionary=True)
        cursor.execute('SELECT * FROM users ORDER BY id')
        users = cursor.fetchall()
        for user in users:
            user['formatted_limit'] = format_size(user['storage_limit'])
            user['formatted_used'] = format_size(user['used_storage'])
            user['usage_percent'] = (user['used_storage'] / user['storage_limit']) * 100 if user['storage_limit'] > 0 else 0
        cursor.close()
        conn.close()
    return render_template('admin.html', users=users, recycle_days=RECYCLE_DAYS)
@app.route('/logout')
def logout():
    session.clear()
    flash('已成功退出登录', 'success')
    return redirect(url_for('login'))
@app.errorhandler(404)
def page_not_found(e):
    return render_template('404.html'), 404
@app.errorhandler(413)
def request_entity_too_large(e):
    flash(f'文件过大，最大支持 {format_size(app.config["MAX_CONTENT_LENGTH"])}', 'error')
    return redirect(url_for('dashboard'))
def format_size(size_bytes):
    """格式化文件大小显示"""
    if size_bytes == 0:
        return "0B"
    size_name = ("B", "KB", "MB", "GB", "TB")
    i = int(math.floor(math.log(size_bytes, 1024)))
    p = math.pow(1024, i)
    s = round(size_bytes / p, 2)
    return f"{s} {size_name[i]}"
app.jinja_env.globals['format_size'] = format_size  # 注册为模板全局函数
def get_user_folders(user_id, parent_id=None):
    """获取用户文件夹（仅当前层级）"""
    folders = []
    conn = get_db_connection()
    if conn:
        cursor = conn.cursor(dictionary=True)
        if parent_id is None:
            cursor.execute('''
            SELECT * FROM folders WHERE user_id = %s AND parent_id IS NULL ORDER BY created_at DESC
            ''', (user_id,))
        else:
            cursor.execute('''
            SELECT * FROM folders WHERE user_id = %s AND parent_id = %s ORDER BY created_at DESC
            ''', (user_id, parent_id))
        folders = cursor.fetchall()
        cursor.close()
        conn.close()
    return folders
def get_folder_path(folder_id):
    """获取文件夹路径"""
    path = []
    if folder_id:
        conn = get_db_connection()
        if conn:
            current_id = folder_id
            while current_id:
                cursor = conn.cursor(dictionary=True)
                cursor.execute('SELECT id, name, parent_id FROM folders WHERE id = %s', (current_id,))
                folder = cursor.fetchone()
                cursor.close()
                if folder:
                    path.insert(0, {'id': folder['id'], 'name': folder['name']})
                    current_id = folder['parent_id']
                else:
                    current_id = None
            conn.close()
    return path

def get_user_zip_dir(user_id):
    """获取用户zip目录路径，自动创建"""
    user_folder = os.path.join(app.config['UPLOAD_FOLDER'], str(user_id))
    zip_dir = os.path.join(user_folder, "zip")
    os.makedirs(zip_dir, exist_ok=True)
    return zip_dir

def zip_folder(user_id, folder_id, folder_name):
    """压缩指定文件夹为zip文件，返回zip文件路径（存放在zip目录）"""
    user_folder = os.path.join(app.config['UPLOAD_FOLDER'], str(user_id))
    zip_dir = get_user_zip_dir(user_id)
    folder_path = os.path.join(user_folder, folder_name)
    zip_name = f"{folder_name or 'folder'}_{folder_id}.zip"
    zip_path = os.path.join(zip_dir, zip_name)
    if not os.path.exists(folder_path):
        return None
    # 压缩文件夹内容（递归）
    with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
        for root, dirs, files in os.walk(folder_path):
            for file in files:
                abs_path = os.path.join(root, file)
                rel_path = os.path.relpath(abs_path, user_folder)
                zipf.write(abs_path, rel_path)
    return zip_path

def ensure_folder_zip(user_id, folder_id, folder_name):
    """确保文件夹zip存在，否则重新压缩（存放在zip目录）"""
    zip_dir = get_user_zip_dir(user_id)
    zip_name = f"{folder_name or 'folder'}_{folder_id}.zip"
    zip_path = os.path.join(zip_dir, zip_name)
    user_folder = os.path.join(app.config['UPLOAD_FOLDER'], str(user_id))
    folder_path = os.path.join(user_folder, folder_name)
    if not os.path.exists(zip_path) or (
        os.path.exists(folder_path) and os.path.getmtime(zip_path) < os.path.getmtime(folder_path)
    ):
        zip_folder(user_id, folder_id, folder_name)
    return zip_path

def remove_folder_zip(user_id, folder_id, folder_name):
    """删除文件夹的zip压缩包（从zip目录）"""
    zip_dir = get_user_zip_dir(user_id)
    zip_name = f"{folder_name or 'folder'}_{folder_id}.zip"
    zip_path = os.path.join(zip_dir, zip_name)
    if os.path.exists(zip_path):
        try:
            os.remove(zip_path)
        except Exception as e:
            print(f"删除压缩包失败: {e}")

@app.route('/download-folder/<int:folder_id>')
@login_required
def download_folder(folder_id):
    user_id = session['user_id']
    conn = get_db_connection()
    folder_name = None
    if conn:
        cursor = conn.cursor(dictionary=True)
        cursor.execute('SELECT name FROM folders WHERE id = %s AND user_id = %s', (folder_id, user_id))
        folder = cursor.fetchone()
        cursor.close()
        conn.close()
        if folder:
            folder_name = folder['name']
    if not folder_name:
        abort(404)
    zip_path = ensure_folder_zip(user_id, folder_id, folder_name)
    if not os.path.exists(zip_path):
        zip_path = zip_folder(user_id, folder_id, folder_name)
    if not zip_path or not os.path.exists(zip_path):
        flash('文件夹压缩失败或文件夹为空', 'error')
        return redirect(url_for('dashboard', folder_id=folder_id))
    zip_dir = get_user_zip_dir(user_id)
    filename = os.path.basename(zip_path)
    return send_from_directory(zip_dir, filename, as_attachment=True)

@app.route('/delete-selected-files', methods=['POST'])
@login_required
def delete_selected_files():
    user_id = session['user_id']
    file_ids = request.form.getlist('file_ids')
    conn = get_db_connection()
    if conn and file_ids:
        total_size = 0
        cursor = conn.cursor(dictionary=True)
        for file_id in file_ids:
            cursor.execute('SELECT * FROM files WHERE id = %s AND user_id = %s', (file_id, user_id))
            file = cursor.fetchone()
            if file:
                total_size += file['file_size']
                cursor.execute('UPDATE files SET is_deleted = 1 WHERE id = %s', (file_id,))
        conn.commit()
        cursor.execute('UPDATE users SET used_storage = used_storage - %s WHERE id = %s', (total_size, user_id))
        flash('选中文件已移入回收站', 'success')
        cursor.close()
        conn.close()
    return redirect(url_for('dashboard'))

def schedule_zip_folder(user_id, folder_id, folder_name):
    """后台异步压缩文件夹"""
    def zip_task():
        zip_folder(user_id, folder_id, folder_name)
    t = threading.Thread(target=zip_task)
    t.daemon = True
    t.start()

def remove_folder_zip(user_id, folder_id, folder_name):
    """删除文件夹的zip压缩包"""
    zip_dir = get_user_zip_dir(user_id)
    zip_name = f"{folder_name or 'folder'}_{folder_id}.zip"
    zip_path = os.path.join(zip_dir, zip_name)
    if os.path.exists(zip_path):
        try:
            os.remove(zip_path)
        except Exception as e:
            print(f"删除压缩包失败: {e}")

if __name__ == '__main__':
    init_db()
    cleanup_old_files()  # 启动清理定时器
    # 关闭自动重载，避免 WinError 10038
    app.run(debug=True, use_reloader=False)